/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  socket_client.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(28/03/25)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "28/03/25 16:30:15"
 *                 
 ********************************************************************************/

#include "socket_client.h"

/*socket初始化*/
int socket_init(socket_cli *sock, char *host, int port)
{
	if(!host || !port )
	{
		return 0;
	}

	sock->sockfd = -1;
	sock->port = port;
	strcpy(sock->host,host);

	return 1;
}

/*关闭socket*/
int socket_close(socket_cli *sock)
{
	if( !sock )
		return -1;

	if( sock->sockfd > 0)
	{
		close(sock->sockfd);
		sock->sockfd = -1;
	}

	return 0;
}

/*socket连接*/
int socket_connect(socket_cli *sock)
{        
	struct sockaddr_in      	servaddr;     
	int                     	rv = -1; 

	sock->sockfd = socket(AF_INET, SOCK_STREAM, 0);     
	if ( sock->sockfd < 0 ) 
	{    
		return -1;      
	}   
	log_debug("create socket fd[%d] successfully\n", sock->sockfd);

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(sock->port);  

	if ( inet_aton(sock->host, &servaddr.sin_addr) == 0 )
	{
		log_debug("Input is not a valid IPv4 address. Attempting to resolve as a domain name...\n");

		struct addrinfo 		hints, *res, *p;		
		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM; 		
		hints.ai_protocol = IPPROTO_TCP;

		int status = getaddrinfo(sock->host, NULL, &hints, &res);
		if (status != 0) 
		{
			log_error("Domain name resolution failed.\n");
			return -1;
		}
		for ( p = res; p != NULL; p = p->ai_next )
		{
			struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
			servaddr.sin_addr = ipv4->sin_addr;

			log_debug("start connect to server [%s:%d]\n", sock->host, sock->port);
			rv=connect(sock->sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
			if ( rv < 0 )
			{   
				log_debug("connect to server failed\n");
				continue;
			}
			else
			{   
				sock->connected = 1;
				log_info("connect to server[%s:%d] successfully!\n", sock->host, sock->port);
				freeaddrinfo(res);  
				return 0;
			}

		}
		freeaddrinfo(res);  
		log_debug("Domain name resolution failed\n", sock->host, sock->port);
		return -1;
	}

	log_debug("start connect to server [%s:%d]\n", sock->host, sock->port);
	rv=connect(sock->sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if ( rv < 0 ) 
	{   
		log_debug("connect to server failed\n");
		socket_close(sock);
		return -1;
	}
	else
	{
		sock->connected = 1;
		log_info("connect to server[%s:%d] successfully!\n", sock->host, sock->port);
	}

	return 0;
}

/*检查连接状态*/
int socket_check(socket_cli *sock)
{
	struct tcp_info     info;
	socklen_t           len = sizeof(info);

	if(getsockopt(sock->sockfd, IPPROTO_TCP, TCP_INFO, &info, &len) != 0)    /*1.套接字文件描述符 2.选项协议层 3.具体选项 4.指向缓冲1区的指针，存储获取的选项值 5.输入时为缓冲区大小，输出时为实际获取的选项值长度*/
	{
		return -2;
	}

	if(info.tcpi_state == TCP_ESTABLISHED)    /*结构体info里有个tcpi_state的成员，存储TCP状态，TCP_ESTABLISHED表示连接已建立，返回0，否则返回-1 */
	{
		return 0;
	}
	else
	{
		return -1;
	}
}

/*读*/
int socket_read(socket_cli *sock)
{
	int				rv = -1;
	char			buf[1024];

	if( !sock )    
	{           
		return -1;
	}       

	if( sock->sockfd< 0 || !sock->connected )   
	{
		return -1;
	}   

	memset(buf, 0, sizeof(buf));
	rv=read(sock->sockfd, buf, sizeof(buf));
	if ( rv < 0 )
	{
		log_warn("Read data from server by fd[%d] failure: %s\n", sock->sockfd, strerror(errno));
		socket_close(sock);
		return -1;
	}
	else if ( rv == 0 )
	{
		log_warn("Socket[%d] get disconnected\n", sock->sockfd);
		socket_close(sock);
		return -1;
	}

	if ( !sizeof(buf) )
	{
		return -1;
	}

	log_info("Read %d bytes data from Server: %s\n", rv, buf);

	return 0;
}

/*写 */
int socket_write(socket_cli *sock, char *data, int len)
{
	int			rv = -1;
	int			left_bytes = len;
	int			i = 0;

	if( !sock || !data || len <= 0 )     
	{           
		return -1;
	}       

	if( sock->sockfd< 0 || !sock->connected )   
	{
		return -1;
	}   

	log_debug("start write to server");

	while( left_bytes > 0 )
	{
		rv = write(sock->sockfd, &data[i], left_bytes);
		if ( rv < 0 )
		{   
			log_warn("write to server by fd[%d] failure: %s\n", sock->sockfd, strerror(errno));
			socket_close(sock);
			return -1;
		}
		else if ( rv == 0 )
		{
			log_debug("sent over\n");
			return 0;
		}

		i += rv;
		left_bytes -= rv;
		continue;
	}

	return 0;
}
